import os.path
import os
import fnmatch

import chardet


class ReadFile(object):

    typedcptuple = ('rar', 'tar', 'tar.gz', 'zip')

    @staticmethod
    def decompressFlie(dir_path: str, decompress=False, typedcp=typedcptuple):
        '''
        comment: 解压 dir_path 目录下的压缩文件
        :param dir_path: 需解压文件的存放目录的路径
        :param decompress: 是否执行解压, 默认 False 不解压
        :param typedcp: 由需解压文件的后缀名组成的元组
        :return: None
        '''
        if not os.path.exists(dir_path) or not os.path.isdir(dir_path):
            raise ValueError('path not exists or not a file')
        if not isinstance(typedcp, (list, tuple)):
            raise TypeError('parameter type error')
        if decompress:
            # 解压
            for filename in os.listdir(dir_path):
                for tdcp in typedcp:
                    tdcpword = f'*.{tdcp}'
                    if fnmatch.fnmatch(filename, tdcpword):
                        cur_file_path = os.path.join(dir_path, filename)
                        dcp_file_path = os.path.join(dir_path, filename.split('.')[0])
                        os.system(f"7z.exe -aoa x {cur_file_path} -o{dcp_file_path}")
        else:
            pass

    @staticmethod
    def traverseDir(dir_path, file_contents, dcp=False, typedcp=typedcptuple):
        ReadFile.decompressFlie(dir_path, decompress=dcp, typedcp=typedcp)
        for cur_name in os.listdir(dir_path):
            cur_path = os.path.join(dir_path, cur_name)
            if os.path.isdir(cur_path):
                ReadFile.traverseDir(cur_path, file_contents, dcp=False, typedcp=typedcp)
            elif os.path.isfile(cur_path):
                filepath = cur_path
                if ReadFile.isFilterFile(filepath, posi='head'):
                    file_content = ReadFile.getContent(filepath, endword='Maps:', elineposi='head')
                    file_contents.append((filepath, file_content))
            else:
                pass
        return file_contents

    @staticmethod
    def readFile(dir_path, dcp=False, typedcp=typedcptuple):
        file_contents = []
        ReadFile.traverseDir(dir_path, file_contents, dcp=dcp, typedcp=typedcp)
        return file_contents

    @staticmethod
    def isFilterFile(filepath: str, word: str = 'cppcrash', posi: str = 'unlimited'):
        '''
        comment: 若 filepath 是一个文件, 并且文件名包含 word, 则返回这个 filepath
        :param filepath: str, 完整的文件路径(含文件名)
        :param word: str, 按 word 匹配文件名
        :param posi: str, word 在文件名中的位置
                unlimited： 不限制位置(默认)
                head: 以 word 开头
                tail: 以 word 结尾
        :return: filepath
        '''
        if not isinstance(filepath, str) or not isinstance(word, str) or \
                not isinstance(posi, str):
            raise TypeError('At least one parameter is of the wrong type')
        if not os.path.exists(filepath) or not os.path.isfile(filepath):
            raise FileNotFoundError('path not exists or not a file')
        if posi not in ['head', 'unlimited', 'tail']:
            raise ValueError("Argument out of optional range: ['head', 'body', 'tail']")
        if posi == 'head':
            matchword = f'{word}*'
        elif posi == 'unlimited':
            matchword = f'*{word}*'
        elif posi == 'tail':
            matchword = f'*{word}'
        else:
            raise ValueError('invalid value')
        filename = os.path.split(filepath)[-1]
        if fnmatch.fnmatch(filename, matchword):
            return True
        else:
            return False

    @staticmethod
    def getContent(filepath, startword: str = None, slineposi: str = 'unlimited', endword: str = None,
                   elineposi: str = 'unlimited'):
        '''
        comment: 用以获取单个文件中的内容
        :param filepath: str, 完整的文件路径(含文件名)
        :param startword: str, 从含有 startword 这一行开始读取
        :param slineposi: slineposi： str, 指定 startword 在一行中的匹配位置
                unlimited： 不限制位置(默认)
                head: 匹配以 startword 开头的行
                tail: 匹配以 startword 结尾的行
        :param endword: str, 从含有 endword 这一行开始读取
        :param elineposi: str, 指定 endword 在一行中的匹配位置
                unlimited： 不限制位置(默认)
                head: 匹配以 endword 开头的行
                tail: 匹配以 endword 结尾的行
        :return: lineslist, 列表, 元素为文件中的行字符串
        '''
        if not isinstance(filepath, str) or (startword is not None and not isinstance(startword, str)) or \
                (endword is not None and not isinstance(endword, str)) or not isinstance(elineposi, str):
            raise TypeError('At least one parameter is of the wrong type')
        if not os.path.exists(filepath) or not os.path.isfile(filepath):
            raise FileNotFoundError('path not exists or not a file')
        if slineposi not in ['head', 'unlimited', 'tail']:
            raise ValueError("Argument out of optional range: ['head', 'body', 'tail']")
        if elineposi not in ['head', 'unlimited', 'tail']:
            raise ValueError("Argument out of optional range: ['head', 'body', 'tail']")
        lineslist = []
        print(filepath)

        # 解决ansi编码问题
        frb = open(filepath, 'rb')
        data = frb.read()
        frb.close()
        if data == b"":
            return []
        encoding = chardet.detect(data)['encoding']
        data_decode = data.decode(encoding, 'ignore')
        data_encode = data_decode.encode('utf-8')
        data_utf8 = data_encode.decode('utf-8')
        data_list = data_utf8.split("\n")
        # with open(filepath, 'r', encoding='utf-8') as fr:

        readstatus = False
        for line in data_list:
            line = line.rstrip()
            # 这里去掉了每一行结尾的换行符
            if readstatus == False:
                if startword is None:
                    readstatus = True
                else:
                    if slineposi == 'head':
                        if line.startswith(startword):
                            readstatus = True
                    elif slineposi == 'unlimited':
                        if startword in line:
                            readstatus = True
                    elif slineposi == 'tail':
                        if line.endswith(startword):
                            readstatus = True
                    else:
                        pass
            else:
                pass
            if readstatus:
                if endword is not None:
                    if elineposi == 'head':
                        if line.startswith(endword):
                            break
                        lineslist.append(line)
                    elif elineposi == 'unlimited':
                        if endword in line:
                            break
                        lineslist.append(line)
                    elif elineposi == 'tail':
                        if line.endswith(endword):
                            break
                        lineslist.append(line)
                    else:
                        pass
                else:
                    lineslist.append(line)
            else:
                pass
        return lineslist
